//! 字符设备后端接口实现
#![feature(lazy_cell)]
#![allow(unsafe_code)]

use std::{
    any::Any,
    collections::HashMap,
    io,
    sync::{
        atomic::{AtomicBool, Ordering},
        Arc, Mutex,
    },
    time::Duration,
};

use error::{SmkError, SmkResult};
use objectid::ObjectId;

pub mod stdio;
pub mod telnet;

/// 字符接口返回结果
///
/// `SmkResult` 别名
pub type IfChrResult<T> = SmkResult<T>;

/// 字符接口错误代码
///
/// `SmkError` 别名
pub type IfChrError = SmkError;

/// 字符设备事件
///
/// 当字符设备触发事件时, 向字符后端发送该事件
#[derive(Debug, PartialEq, Eq)]
pub enum CharDeviceEvent {
    /// 触发 break 字符事件
    EventBreak,
    /// 新的连接被建立
    EventOpened,
    /// 多路复用聚焦设置在该字符后端
    EventMuxIn,
    /// 当前多路复用将被移出(一般是回到主控台)
    EventMuxOut,
    /// 连接被关闭
    EventClosed,
}

/// 字符后端事件实现
///
/// 当字符设备有数据输入时向字符后端通知该事件或者发送数据
pub trait CharBackendImpl: Send + Sync + ObjectId {
    /// 可以从字符设备接收的字符数量
    fn backend_can_recv(&self) -> usize;
    /// 从字符设备接收字符
    fn backend_receive(&self, buf: &[u8]);
    /// 字符设备通知事件到达
    fn backend_event(&self, event: CharDeviceEvent);
}

/// 字符设备 ioctl 命令
///
/// 字符后端可以调用特定于字符设备的 ioctl 命令
pub enum CharDeviceIoctl {
    /// 设置 break
    SerialSetBreak = 2,
}

/// 字符后端设备
///
/// 大多数情况下字符后端设备就是字符设备
pub struct CharBackendDevice {
    char_device: Arc<dyn CharDeviceImpl + Send + Sync>,
}

impl CharBackendDevice {
    /// 创建一个字符后端设备
    pub fn create(char_device: Arc<dyn CharDeviceImpl + Send + Sync>) -> Self {
        CharBackendDevice { char_device }
    }

    /// 向字符后端设备写入数据
    ///
    /// # Errors
    /// 由字符设备返回错误
    pub fn chr_write(&self, buf: &[u8]) -> io::Result<usize> {
        self.char_device.write(buf)
    }

    /// 向字符后端设备执行 ioctl
    ///
    /// # Errors
    /// 由字符设备返回错误
    pub fn chr_ioctl(&self, cmd: CharDeviceIoctl, data: &mut Box<dyn Any>) -> IfChrResult<()> {
        self.char_device.ioctl(cmd, data)
    }

    /// 向字符后端设备通知可以接收字符
    pub fn chr_notify_accept_input(&self) {
        self.char_device.notify_accept_input();
    }
}

/// 字符设备轮询事件
pub enum PollEvent {
    /// break 事件
    Break,
}

/// 字符设备轮询状态
pub enum PollStatus {
    /// 就绪
    Ready,
    /// 超时
    Timeout,
    /// 连接被断开
    ConnectAborted,
    /// 事件组
    Event(Vec<PollEvent>),
}

/// 字符设备轮询状态(外部)
#[derive(Debug, PartialEq, Eq)]
pub enum CharPollStatus {
    /// 就绪
    Ready,
    /// 超时
    Timeout,
    /// 连接被断开
    ConnectAborted,
}

/// 字符设备实现
#[allow(unused_variables)]
pub trait CharDeviceImpl {
    /// 设备预备初始化工作
    ///
    /// # Errors
    /// 由字符设备返回错误
    fn prepare(&self) -> io::Result<()> {
        Ok(())
    }
    /// 设备进入等待连接被建立超时状态
    ///
    /// 返回`true`完成等待, 反之超时
    ///
    /// # Errors
    /// 由字符设备返回错误
    fn wait(&self, timeout: Duration) -> io::Result<bool> {
        // 不一定所有设备都需要该状态
        Ok(true)
    }
    /// 设备进入轮询事件超时状态
    ///
    /// 返回`true`事件到来, 反之超时
    ///
    /// # Errors
    /// 由字符设备返回错误
    fn poll(&self, timeout: Duration) -> io::Result<PollStatus>;
    /// 字符设备可以读取的字节数量
    fn can_read(&self) -> usize;
    /// 从设备读取数据
    ///
    /// # Errors
    /// 由字符设备返回错误
    fn read(&self, buf: &mut [u8]) -> io::Result<usize>;
    /// 向设备写入数据
    ///
    /// # Errors
    /// 由字符设备返回错误
    fn write(&self, buf: &[u8]) -> io::Result<usize>;
    /// 通知字符设备可以接收数据
    fn notify_accept_input(&self);
    /// 向设备发送一个特定于设备的 ioctl
    ///
    /// # Errors
    /// 由字符设备返回错误
    fn ioctl(&self, cmd: CharDeviceIoctl, data: &mut Box<dyn Any>) -> IfChrResult<()> {
        Ok(())
    }
    /// 设备工作结束
    ///
    /// # Errors
    /// 由字符设备返回错误
    fn end(&self) -> io::Result<()>;
}

/// 字符设备读取信号
pub enum CharDeviceSignal {
    /// 完成信号处理
    None,
    /// 请求退出程序
    ReqExit,
    /// 切换回监视器
    ReqSwitch,
}

// ctrl-a 被用于逃逸字符
const TERM_GOT_ESCAPE: u8 = 0x1;

static HELP_BYTES: [&str; 6] = [
    "\n\r",
    "CtrlA + h|?  print this help\n\r",
    "CtrlA + x    exit emulator\n\r",
    "CtrlA + b    send break (magic sysrq)\n\r",
    "CtrlA + c    swtich to monitor\n\r",
    "CtrlA + CtrlA  send CtrlA\n\r",
];

enum ProcByte {
    // 需要发送该字符
    Send,
    // 跳过发送该字符
    Skip,
    // 跳过发送该字符并且请求退出程序
    ReqExit,
    // 跳过发送该字符并且请求切换回监视器
    ReqSwtich,
}

/// 字符设备
pub struct CharDevice<D: CharDeviceImpl> {
    char_device: Arc<D>,
    char_backend: Arc<CharBackendMap>,
    current: Arc<dyn CharBackendImpl>,
    monitor: Option<Arc<dyn CharBackendImpl>>,
    term_got_escape: bool,
}

impl<D: CharDeviceImpl> CharDevice<D> {
    /// 创建一个字符设备
    ///
    /// # Panics
    /// 如果字符后端不存在将会 panic
    pub fn create(char_device: Arc<D>, char_backend_map: Arc<CharBackendMap>) -> Self {
        let current = char_backend_map.current().unwrap();
        current.backend_event(CharDeviceEvent::EventMuxIn);
        CharDevice {
            char_device,
            char_backend: char_backend_map,
            current,
            monitor: None,
            term_got_escape: false,
        }
    }

    /// 设置一个字符后端为监视器
    pub fn set_monitor(&mut self, id: &str) {
        self.monitor = self.char_backend.char_backend(id);
    }

    /// 更新当前字符后端
    pub fn update_current(&mut self) -> Option<()> {
        if self.char_backend.changed() {
            self.current = self.char_backend.current()?;
            self.char_backend.clear_changed();
            self.current.backend_event(CharDeviceEvent::EventMuxIn);
        }
        Some(())
    }

    /// 聚焦切换到设置的监视器上
    pub fn switch_to_monitor(&mut self) -> Option<()> {
        if let Some(mon) = self.monitor.as_ref() {
            self.char_backend.select_current(mon.id())?;
        }
        self.update_current()
    }

    /// 返回当前监视器
    pub fn current(&self) -> &str {
        self.current.id()
    }

    /// 设备预备
    ///
    /// # Errors
    /// 由字符设备返回错误
    pub fn prepare(&self) -> io::Result<()> {
        self.char_device.prepare()
    }

    /// 设备等待连接被建立
    ///
    /// # Errors
    /// 由字符设备返回错误
    pub fn wait(&self, timeout: Duration) -> io::Result<bool> {
        let res = self.char_device.wait(timeout)?;
        if res {
            self.current.backend_event(CharDeviceEvent::EventOpened);
        }
        Ok(res)
    }

    /// 设备轮询
    ///
    /// # Errors
    /// 由字符设备返回错误
    pub fn poll(&self, timeout: Duration) -> io::Result<CharPollStatus> {
        let res = self.char_device.poll(timeout)?;
        match res {
            PollStatus::Ready => {},
            PollStatus::Timeout => {
                return Ok(CharPollStatus::Timeout);
            },
            PollStatus::ConnectAborted => {
                self.current.backend_event(CharDeviceEvent::EventClosed);
                return Ok(CharPollStatus::ConnectAborted);
            },
            PollStatus::Event(events) => {
                for event in events {
                    match event {
                        PollEvent::Break => {
                            self.current.backend_event(CharDeviceEvent::EventBreak);
                        },
                    }
                }
            },
        }
        Ok(CharPollStatus::Ready)
    }

    fn print_help(&self) -> io::Result<()> {
        for i in HELP_BYTES {
            self.char_device.write(i.as_bytes())?;
        }
        Ok(())
    }

    fn proc_byte(&mut self, ch: u8) -> ProcByte {
        if self.term_got_escape {
            self.term_got_escape = false;
            if ch == TERM_GOT_ESCAPE {
                return ProcByte::Send;
            }
            match ch {
                b'h' | b'?' => {
                    let _ = self.print_help();
                },
                b'x' => {
                    return ProcByte::ReqExit;
                },
                b'b' => {
                    self.current.backend_event(CharDeviceEvent::EventBreak);
                },
                b'c' => {
                    self.current.backend_event(CharDeviceEvent::EventMuxOut);
                    return ProcByte::ReqSwtich;
                },
                _ => {},
            }
        } else if ch == TERM_GOT_ESCAPE {
            self.term_got_escape = true;
        } else {
            return ProcByte::Send;
        }
        ProcByte::Skip
    }

    /// 从设备读取一个字符
    ///
    /// # Errors
    /// 由字符设备返回错误
    pub fn handle(&mut self) -> io::Result<CharDeviceSignal> {
        let count = self.char_device.can_read();
        for _ in 0..count {
            if self.current.backend_can_recv() == 0 {
                break;
            }
            let mut buf = [0u8; 1];
            self.char_device.read(&mut buf)?;
            match self.proc_byte(buf[0]) {
                ProcByte::Send => {
                    self.current.backend_receive(&buf);
                },
                ProcByte::Skip => {},
                ProcByte::ReqExit => {
                    return Ok(CharDeviceSignal::ReqExit);
                },
                ProcByte::ReqSwtich => {
                    return Ok(CharDeviceSignal::ReqSwitch);
                },
            }
        }
        Ok(CharDeviceSignal::None)
    }

    /// 设备结束工作
    ///
    /// # Errors
    /// 由字符设备返回错误
    pub fn end(&self, status: CharPollStatus) -> io::Result<()> {
        if status != CharPollStatus::ConnectAborted {
            self.current.backend_event(CharDeviceEvent::EventClosed);
        }
        self.char_device.end()
    }
}

/// 字符后端管理表
#[derive(Default)]
pub struct CharBackendMap {
    #[allow(clippy::type_complexity)]
    char_backend: Mutex<(String, HashMap<String, Arc<dyn CharBackendImpl>>)>,
    char_device_impl: Mutex<Option<Arc<dyn CharDeviceImpl + Send + Sync>>>,
    changed: AtomicBool,
}

impl CharBackendMap {
    /// 注册统一的字符设备实现
    #[allow(clippy::missing_panics_doc)]
    pub fn register_char_device_impl(
        &self,
        char_device_impl: Arc<dyn CharDeviceImpl + Send + Sync>,
    ) {
        let mut lock = self.char_device_impl.lock().unwrap();
        if lock.is_none() {
            *lock = Some(char_device_impl);
        }
    }

    /// 获取当前字符后端的字符设备
    ///
    /// # Panics
    /// 如果当前字符后端表没有设置字符设备将会 panic
    pub fn char_device_impl(&self) -> Arc<dyn CharDeviceImpl + Send + Sync> {
        let lock = self.char_device_impl.lock().unwrap();
        assert!(lock.is_some());
        if let Some(l) = lock.as_ref() {
            return l.clone();
        }
        unreachable!();
    }

    /// 存放一个新的字符后端到该管理字符设备表
    #[allow(clippy::missing_panics_doc)]
    pub fn register_char_backend(&self, char_backend: Arc<dyn CharBackendImpl>) {
        let mut lock = self.char_backend.lock().unwrap();
        let exist = lock.1.insert(char_backend.id().to_string(), char_backend);
        assert!(exist.is_none());
    }

    /// 选择一个字符后端为当前设备
    #[allow(clippy::missing_panics_doc)]
    pub fn select_current(&self, id: &str) -> Option<bool> {
        let mut lock = self.char_backend.lock().unwrap();
        if lock.0 == *id {
            return Some(false);
        }

        let current = lock.1.get(id)?;
        lock.0 = current.id().to_string();
        self.changed.store(true, Ordering::Relaxed);
        Some(true)
    }

    /// 遍历当前 Map
    #[allow(clippy::missing_panics_doc)]
    pub fn for_each<F>(&self, mut f: F)
    where
        F: FnMut(&str, bool),
    {
        let lock = self.char_backend.lock().unwrap();
        lock.1.iter().for_each(|(c, _)| f(c, lock.0.eq(c)));
    }

    // 返回当前字符后端
    fn current(&self) -> Option<Arc<dyn CharBackendImpl>> {
        let lock = self.char_backend.lock().unwrap();
        let current = lock.1.get(&lock.0)?;
        Some(current.clone())
    }

    // 返回选择的字符后端
    fn char_backend(&self, id: &str) -> Option<Arc<dyn CharBackendImpl>> {
        let lock = self.char_backend.lock().unwrap();
        let char_backend = lock.1.get(id)?;
        Some(char_backend.clone())
    }

    // 是否更新了字符后端
    fn changed(&self) -> bool {
        self.changed.load(Ordering::Relaxed)
    }

    // 清除更新
    fn clear_changed(&self) {
        self.changed.store(false, Ordering::Relaxed);
    }
}
